home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / opsprd.zip / OPSPREAD.DOC next >
Text File  |  1992-04-13  |  23KB  |  519 lines

  1.                 OPSPREAD - Spreadsheet-like PickLists
  2.                Copyright (c) 1992 TurboPower Software
  3.                             January 1992
  4.  
  5. ------- Overview ----------------------------------------------------
  6.  
  7. A common request for Object Professional has been the ability to
  8. display or input information in a format similar to that of a
  9. spreadsheet. It has always been possible to do this using an
  10. EntryScreen or a ScrollingEntryScreen, but for spreadsheets of any
  11. significant size the speed penalty and memory overhead are high
  12. because of the flexibility inherent to an entry screen. A better tool
  13. for this task is the PickList object, which has always had hooks to
  14. offer different "list orientations". It is fairly straightforward to
  15. use these hooks to make a pick list that has scrolling properties like
  16. those of a spreadsheet.
  17.  
  18. OPSPREAD is a unit that derives a new object SpreadList from the
  19. PickList. With a few minor exceptions, it inherits all of the
  20. capabilities of the PickList, including protected items, flexwriting,
  21. multiple choice lists, mouse support, scroll bars, resizability,
  22. stream support, and so on. Because it inherits so much capability from
  23. PickList, SpreadList adds very little code to a program: the entire
  24. unit has about 4000 bytes of code, and some of that will be stripped
  25. by the smart linker.
  26.  
  27. The SpreadList does not have built-in capability for item data entry
  28. and expression evaluation. Exactly what your spreadsheet does is
  29. specific to your program, and we didn't attempt to guess what that is.
  30. Hooks inherent to the PickList make it easy for you to get control
  31. when needed to accept keyboard entry, validate cells, draw scrolling
  32. headers, and so on. One of the demo programs provided here shows you
  33. how to implement these features in a typical application.
  34.  
  35. Because SpreadList is based on PickList, the total number of cells is
  36. limited to 65535, or 254 on a side for a square sheet. Also, unlike
  37. commercial spreadsheets, the columns of a SpreadList must all have the
  38. same width.
  39.  
  40. NOTE: OPSPREAD requires Object Professional version 1.13 of later to
  41. compile and use it successfully.
  42.  
  43.  
  44. ------- Using the SpreadList ----------------------------------------
  45.  
  46. The SpreadList object provides only a few new methods, so there's not
  47. much new to learn. Here's a simple example program that shows how to
  48. use the object.
  49.  
  50. program ExSpread;
  51. uses
  52.   OpCrt, OpString, OpRoot, OpCmd, OpFrame, OpWindow, OpPick, OpSpread;
  53. const
  54.   SpreadSize = 50;  {SpreadList contains SpreadSize*SpreadSize cells}
  55.   ItemWidth = 7;    {Each item displays ItemWidth characters}
  56. var
  57.   SL : SpreadList;
  58.  
  59. {$F+}
  60. procedure SpreadItem(Item : Word; Mode : pkMode;
  61.                      var IType : pkItemType; var IString : String;
  62.                      PickPtr : PickListPtr);
  63. var
  64.   Row : Word;
  65.   Col : Word;
  66. begin
  67.   with SpreadListPtr(PickPtr)^ do begin
  68.     Row := GetItemRow(Item);
  69.     Col := GetItemCol(Item);
  70.     IString := Long2Str(Row)+','+Long2Str(Col);
  71.   end;
  72. end;
  73. {$F-}
  74.  
  75. begin
  76.   SL.InitCustom(5, 3, 32, 20,
  77.                 DefaultColorSet,
  78.                 DefWindowOptions or wBordered,
  79.                 ItemWidth, SpreadSize, SpreadSize,
  80.                 SpreadItem, SingleChoice);
  81.   if InitStatus <> 0 then begin
  82.     WriteLn('Error initializing SpreadList');
  83.     Halt;
  84.   end;
  85.  
  86.   {Process it, then finish up}
  87.   SL.Process;
  88.   SL.Erase;
  89.   SL.Done;
  90. end.
  91.  
  92. Focus first on the call to SpreadList's constructor, InitCustom. Most
  93. of the parameters are identical to what you would pass to a PickList:
  94. window coordinates, colors, window options, and so on. The main
  95. difference from calling a PickList constructor is that instead of
  96. passing the total number of items in the list, you pass the number of
  97. items on each axis of the spreadsheet. In this example, we're using a
  98. square spreadsheet, and we pass SpreadSize (50) for the number of rows
  99. and columns. The other difference is that there's no need to pass an
  100. orientation (e.g., PickVertical) to the SpreadList, since it has a
  101. built-in orientation that gives it scrolling properties like those of
  102. a spreadsheet.
  103.  
  104. Now take a look at the item string procedure, named SpreadItem in this
  105. example. This routine is declared exactly like one used with a
  106. PickList. There are two differences in what it must do internally:
  107. First, it must typecast the PickPtr parameter to type SpreadListPtr so
  108. that it can refer to unique methods of the SpreadList. Second, it
  109. usually must decompose the Item parameter passed to it into the
  110. component Row and Col numbers of the spreadsheet. The SpreadList
  111. object provides two methods for this purpose: GetItemRow and
  112. GetItemCol. In this example, each item string is assigned the Row,Col
  113. coordinate of the SpreadList cell.
  114.  
  115. The example program goes on to process, then erase and dispose of the
  116. SpreadList.
  117.  
  118. If you run the example, you'll see that the spreadsheet cells are
  119. labeled in just the order you would expect, and that scrolling works
  120. just like a spreadsheet. If you are very familiar with PickList
  121. behavior, you'll notice a couple of minor differences in the cursor
  122. keypad handling. In the PickList, pressing <Home> and <End> moves the
  123. highlight bar to the first and last element in the list, respectively.
  124. In the SpreadList, <Home> and <End> move to the first and last cells
  125. on the current row. <CtrlPgUp> and <CtrlPgDn> move to the top and
  126. bottom cells of the current column, whereas these keystrokes are
  127. ignored by the PickList.
  128.  
  129. More thorough examples are provided in the OPSPREAD archive.
  130. TSPREAD.PAS demonstrates mouse and dragging support, vertical divider
  131. bars, scroll bars, and the use of the GetItemNum method to position
  132. the highlight to a particular cell before calling Process.
  133.  
  134. TSPREAD2.PAS derives a new object, SpreadSheet, from SpreadList.
  135. SpreadSheet implements all the features needed for a simple
  136. spreadsheet application. It shows how to associate a data structure
  137. with each cell of the sheet, and how to edit user input for each cell.
  138. It also demonstrates a completely object-oriented approach to using
  139. OPSPREAD: No procedure pointers are used; instead the SpreadSheet
  140. object overrides virtual methods where appropriate. The data for each
  141. cell is stored in a two-dimensional array of strings, which is also
  142. contained within the SpreadSheet object. The item string method simply
  143. returns the string at the corresponding position within this array. Of
  144. course, the spreadsheet data structure could be more complex, but the
  145. item string procedure would still build a string to display from the
  146. cell data for the specified item position.
  147.  
  148. Input editing is handled using two of the capabilities inherent to the
  149. PickList. First, TSPREAD2 assigns the <Backspace> key as a user exit
  150. command, so that PickList.ProcessSelf will exit whenever this key is
  151. pressed. When this exit command is detected, TSPREAD2 deletes the last
  152. character in the string associated with the current cell. TSPREAD2
  153. also overrides PickList's ItemSearch method, in such a way that
  154. PickList.ProcessSelf will exit whenever any non-extended key, with an
  155. ASCII code 32 or greater, is pressed. TSPREAD2 detects this by
  156. checking for the ccChar exit command; when it sees this exit command,
  157. it validates the character (only numbers are accepted here) and
  158. appends it to the string associated with the current cell. If the
  159. character is not a number or the string is full, TSPREAD2 beeps. The
  160. SpreadSheet object overrides the ProcessSelf method to hide these
  161. editing details from any client program that uses it.
  162.  
  163. Many commercial spreadsheets allow simple editing of cell contents
  164. using keystrokes such as those just described. When another function
  165. key is pressed (<F2> in Lotus 1-2-3), however, a more advanced line
  166. editor is used. The same functionality could be added to SpreadSheet
  167. by mapping <F2> to a user exit command, and instantiating an OPRO
  168. LineEditor to edit the contents of the current cell before returning
  169. to SpreadSheet.Process.
  170.  
  171. TSPREAD2 overrides the PreMove method of PickList in order to draw
  172. scrolling row and column headers on the spreadsheet. Since PickList
  173. calls this method whenever it is ready to get the next command from
  174. the keyboard or mouse, it is a convenient hook for drawing these
  175. headers. SpreadSheet.PreMove checks a couple of variables within the
  176. SpreadSheet object to minimize how often it redraws the headers. It
  177. also calls the TopLeftRowCol method of SpreadList to determine the
  178. spreadsheet row and column currently displayed in the top left corner
  179. of the sheet. TSPREAD2 also overrides the UpdateContents method of
  180. PickList in order to update the scrolling headers when the window is
  181. resized using the mouse.
  182.  
  183. TSPREAD2 overrides two additional virtual methods that were added to
  184. the PickList object just to make writing spreadsheets easier. Function
  185. OKToChangeChoice is called whenever the PickList ProcessSelf method is
  186. ready to move the highlight bar off of one item and on to another.
  187. This hook is ideal for validating the contents of a spreadsheet cell.
  188. TSPREAD2's implementation of this routine assures that the cell is not
  189. empty; if it is, TSPREAD2 writes a warning message, waits for a key to
  190. be pressed, then forces PickList to leave the cursor on the current
  191. cell. Note that OKToChangeChoice should first confirm that the current
  192. choice will really change as a result of the last entered command.
  193. PickList doesn't spend the time to weed out events such as the user
  194. clicking the mouse over the current item, or pressing <Home> when the
  195. cursor is already in column 1. The EvaluateCmd method of PickList is
  196. ideal for this purpose, as shown in SpreadSheet.OKToChangeChoice.
  197.  
  198. TSPREAD2 also overrides the PositionCursor method of PickList. By
  199. default, PickList.PositionCursor positions the hardware cursor on the
  200. first character of each pick item. This isn't usually important
  201. because the cursor is hidden anyway. The SpreadSheet Init constructor
  202. makes the hardware cursor visible to make input editing more
  203. intuitive, so it is important to put the cursor in a sensible
  204. location. SpreadSheet.PositionCursor shows how.
  205.  
  206. Although the SpreadSheet object demonstrated by TSPREAD2 doesn't do
  207. everything you might need, it shows the fundamental techniques used to
  208. add a spreadsheet to your application.
  209.  
  210.  
  211. ------- OPSPREAD Reference Section ----------------------------------
  212.  
  213. Constants
  214. ---------
  215.   pkSpread          = 4;
  216.  
  217. This constant continues the series begun with pkNoOrient, pkVertical,
  218. pkHorizontal, and pkSnaking in OPPICK.PAS. It indicates the
  219. "orientation" of the PickList and will be returned by the
  220. GetOrientation method when called for a SpreadList. You generally
  221. won't need to use the pkSpread constant.
  222.  
  223.   otSpreadList      = 998;   {object type}
  224.   veSpreadList      = 0;     {version code}
  225.   ptPickSpread      = 998;   {pointer code for the pkSpread
  226.                               orientation}
  227.  
  228. These constants are used by the Object Professional stream manager
  229. when a SpreadList instance is stored in a stream. You generally won't
  230. need to refer to them.
  231.  
  232. Types
  233. -----
  234.   SpreadListPtr = ^SpreadList;
  235.   SpreadList =
  236.     object(PickList)
  237.       slRows : Word;
  238.       slCols : Word;
  239.       ...
  240.     end;
  241.  
  242. The object used to implement spreadsheet-like behavior. slRows and
  243. slCols store the number of "cells" along each axis. You shouldn't
  244. modify these fields.
  245.  
  246. SpreadList Methods
  247. ------------------
  248. SpreadList overrides two methods of PickList in order to disable
  249. them. These methods are:
  250.  
  251.   procedure ChangeNumItems(NumItems : Word);
  252.     {-Change the number of items to display}
  253.   procedure ChangeOrientation(Orientation : pkGenlProc);
  254.     {-Change the orientation}
  255.  
  256. If you call these methods, a runtime error 211 will result. Once a
  257. SpreadList is instantiated, you cannot change the number of items in
  258. it, or its orientation.
  259.  
  260. With these two exceptions, SpreadList inherits all of the methods of
  261. the PickList, CommandWindow, StackWindow, RawWindow, and Frame
  262. objects. Refer to the documentation for those objects for further
  263. information.
  264.  
  265. The following sections document methods that are new to SpreadList.
  266.  
  267. Declaration
  268.   function GetItemCol(Item : Word) : Word;
  269. Purpose
  270.   Return the column position of the specified item.
  271. Description
  272.   Given an Item number in the range 1 to slRows*slCols, GetItemCol
  273.   returns the column number in the range 1 to slCols.
  274. See Also
  275.   GetItemRow
  276.  
  277. Declaration
  278.   function GetItemNum(Row, Col : Word) : Word;
  279. Purpose
  280.   Return the item number corresponding to Row and Col.
  281. Description
  282.   Given valid Row and Col numbers, GetItemNum returns the linear item
  283.   number. The value equals (Row-1)*slCols+Col. From this you can see
  284.   that items are numbered in the following fashion (for slRows=3,
  285.   slCols=5):
  286.                   Col
  287.              1  2  3  4  5
  288.           +---------------
  289.     Row 1 |  1  2  3  4  5
  290.     Row 2 |  6  7  8  9 10
  291.     Row 3 | 11 12 13 14 15
  292.  
  293. Declaration
  294.   function GetItemRow(Item : Word) : Word;
  295. Purpose
  296.   Return the row position of the specified item.
  297. Description
  298.   Given an Item number in the range 1 to slRows*slCols, GetItemRow
  299.   returns the row number in the range 1 to slRows.
  300. See Also
  301.   GetItemCol
  302.  
  303. Declarations
  304.   constructor Init(X1, Y1, X2, Y2 : Byte;
  305.                    ItemWidth : Byte;
  306.                    NumRows : Word;
  307.                    NumCols : Word;
  308.                    StringProc : pkStringProc;
  309.                    CommandHandler : pkGenlProc);
  310.   constructor InitCustom(X1, Y1, X2, Y2 : Byte;
  311.                          var Colors : ColorSet;
  312.                          Options : LongInt;
  313.                          ItemWidth : Byte;
  314.                          NumRows : Word;
  315.                          NumCols : Word;
  316.                          StringProc : pkStringProc;
  317.                          CommandHandler : pkGenlProc);
  318.   constructor InitAbstract(X1, Y1, X2, Y2 : Byte;
  319.                            var Colors : ColorSet;
  320.                            Options : LongInt;
  321.                            ItemWidth : Byte;
  322.                            NumRows : Word;
  323.                            NumCols : Word;
  324.                            CommandHandler : pkGenlProc);
  325.   constructor InitDeluxe(X1, Y1, X2, Y2 : Byte;
  326.                          var Colors : ColorSet;
  327.                          Options : LongInt;
  328.                          ItemWidth : Byte;
  329.                          NumRows : Word;
  330.                          NumCols : Word;
  331.                          StringProc : pkStringProc;
  332.                          CommandHandler : pkGenlProc;
  333.                          PickOptions : Word);
  334.   constructor InitAbstractDeluxe(X1, Y1, X2, Y2 : Byte;
  335.                                  var Colors : ColorSet;
  336.                                  Options : LongInt;
  337.                                  ItemWidth : Byte;
  338.                                  NumRows : Word;
  339.                                  NumCols : Word;
  340.                                  CommandHandler : pkGenlProc;
  341.                                  PickOptions : Word);
  342. Purpose
  343.   Initialize a SpreadList.
  344. Description
  345.   These constructors initialize a SpreadList with various degrees of
  346.   control over options and colors.
  347.  
  348.   X1, Y1, X2, and Y2 specify the screen coordinates of the active
  349.   portion of the window. Colors specifies the video attributes to use.
  350.   SpreadList uses the attributes in exactly the same way as a PickList
  351.   does. Options is a bit mask specifying the window options (see page
  352.   4-60 of volume 1).
  353.  
  354.   ItemWidth is the number of screen columns used for each item column.
  355.   For the best appearance, the width of the window (X2-X1+1) should be
  356.   an exact multiple of ItemWidth, although the SpreadList will
  357.   function correctly even if it is not. Remember that if vertical
  358.   divider bars are enabled, the last character position in ItemWidth
  359.   is reserved for the divider.
  360.  
  361.   NumRows and NumCols are the number of rows and columns in the
  362.   spreadsheet. SpreadList will scroll horizontally and vertically to
  363.   display any cells that don't fit within the window. The product
  364.   NumRows*NumCols must be less than 65536 or the constructor will fail
  365.   with error epFatal+ecBadParam.
  366.  
  367.   StringProc is a procedure whose parameters match those of type
  368.   pkStringProc. It must be compiled FAR and not nested within any
  369.   other procedures. Given the item number passed to it as a parameter,
  370.   the StringProc must return a string to display for that item. In the
  371.   context of a spreadsheet, the StringProc will generally first
  372.   compute the Row and Col numbers that correspond to the item number
  373.   by calling GetItemRow and GetItemCol. See the introductory example
  374.   procedure SpreadItem in this file. The StringProc may also take any
  375.   of the actions described for a PickList's StringProc, including
  376.   marking items as protected or semiprotected. All of the constructors
  377.   with Abstract in their name require that a descendant of SpreadList
  378.   override the ItemString method of PickList instead of passing the
  379.   StringProc parameter to the constructor.
  380.  
  381.   CommandHandler takes on one of the values SingleChoice or
  382.   MultipleChoice. This determines whether the PickList allocates a
  383.   BitSet to keep track of selected cells and accepts additional
  384.   commands to select and deselect items. For a SpreadList, the value
  385.   of CommandHandler will generally be SingleChoice.
  386.  
  387.   PickOptions is a bit mask that specifies options specific to a
  388.   PickList. This mask is interpreted the same for a SpreadList
  389.   as it is for a PickList, except that the pkAlterPageRow and
  390.   pkNoHighlightPad options are ignored for a SpreadList.
  391. See Also
  392.   Load
  393.  
  394. Declaration
  395.   {$IFDEF UseStreams}
  396.   constructor Load(var S : IdStream);
  397. Purpose
  398.   Load a spread list from a stream.
  399. Description
  400.   SpreadList.Load works exactly like PickList.Load, except that it
  401.   initializes the additional slRows and slCols fields of the object.
  402.  
  403.   Use procedure SpreadListStream to register types needed for reading
  404.   SpreadList objects from a stream. Note that SpreadListStream
  405.   registers the PickList orientation automatically, so there is no
  406.   need for you to do so. You must still register the SingleChoice or
  407.   MultipleChoice command handler, the StringProc (when you haven't
  408.   overridden ItemString), and any character search or premove routines
  409.   you are using.
  410. See Also
  411.   Store
  412.  
  413. Declaration
  414.   function PickList.OKToChangeChoice : Boolean; virtual;
  415. Purpose
  416.   Called just prior to changing to a new choice in PickList.Process.
  417. Description
  418.   The default version of this method is implemented in the PickList
  419.   object, not SpreadList. It is documented here because it is most
  420.   likely to be overridden by users of SpreadList.
  421.  
  422.   The default OKToChangeChoice always returns True, meaning that the
  423.   highlight bar can be moved off of the current item regardless of its
  424.   contents. In a spreadsheet application, where the contents of each
  425.   item can be changed by the user, it may be necessary to validate the
  426.   contents of the current cell before allowing the user to leave it.
  427.   If so, you should override OKToChangeChoice. The method should
  428.   return True if the current cell (identified by calling
  429.   GetLastChoice) is valid, or False if it is invalid. When
  430.   OKToChangeChoice returns False, PickList leaves the highlight bar on
  431.   the current item and ignores whatever command was last entered.
  432.  
  433.   If you wish to tell the user that the current cell is invalid,
  434.   OKToChangeChoice should do so by beeping or displaying a warning
  435.   line or dialog window. If it displays a warning it must restore the
  436.   screen before returning.
  437.  
  438.   PickList.Process may call OKToChangeChoice in cases where the
  439.   highlight bar won't be leaving the current item. For example, if the
  440.   user clicks the mouse on a scroll bar, and the mouse position is
  441.   such that the new item will be the same as the old item, Process
  442.   will still call OKToChangeChoice. Determining that the current item
  443.   remains unchanged takes additional CPU time that PickList.Process
  444.   needn't spend for most PickList applications. As a result,
  445.   OKToChangeChoice should check that the item will truly change before
  446.   it validates the current cell. The EvaluateCmd method of PickList is
  447.   ideal for doing this, since it simulates what would occur if a
  448.   specified command were executed.
  449.  
  450.   Note that PickList.Process does *not* call OKToChangeChoice when an
  451.   exit command is received via the keyboard or mouse. Therefore it is
  452.   your application's responsibility to validate the current cell when
  453.   Process exits. PickList doesn't do so because some exit commands may
  454.   allow the user to edit the cell further (as the <BackSpace> key does
  455.   in TPSPREAD2.PAS), or may be intended to perform an unrelated
  456.   function and then immediately return to the spreadsheet's Process
  457.   method.
  458.  
  459.   There is no procedure pointer that corresponds to this virtual
  460.   method.
  461.  
  462.   See the OKToChangeChoice method in TSPREAD2.PAS for an example.
  463.  
  464. Declaration
  465.   procedure PickList.PositionCursor(Item : Word;
  466.                                     ACol, ARow : Byte); virtual;
  467. Purpose
  468.   Position the hardware cursor for selected item.
  469. Description
  470.   The default version of this method is implemented in the PickList
  471.   object, not SpreadList. It is documented here because it is most
  472.   likely to be overridden by users of SpreadList.
  473.  
  474.   Item is the number of the item that is currently selected. ACol and
  475.   ARow are the absolute screen position of the first character of the
  476.   item, including any leading pad characters. The default
  477.   PositionCursor simply places the hardware cursor at ARow,ACol by
  478.   calling GoToXYAbs(ACol, ARow). This is more than adequate for normal
  479.   PickList applications since the hardware cursor is usually hidden
  480.   anyway.
  481.  
  482.   For a SpreadList application, it may be desirable to position the
  483.   hardware cursor at an alternate position, for example at the end of
  484.   the string representing the contents of the cell. To do so, override
  485.   PositionCursor and position the cursor as needed.
  486.  
  487.   There is no procedure pointer that corresponds to this virtual
  488.   method.
  489.  
  490.   See the PositionCursor method in TSPREAD2.PAS for an example.
  491.  
  492. Declaration
  493.   {$IFDEF UseStreams}
  494.   procedure Store(var S : IdStream);
  495. Purpose
  496.   Store a spread list in a stream.
  497. Description
  498.   SpreadList.Store works exactly like PickList.Store, except that it
  499.   stores the additional slRows and slCols fields of the object.
  500.  
  501.   See Load for additional information on pointer registration
  502.   requirements of SpreadList.Store.
  503. See Also
  504.   Load
  505.  
  506. Declaration
  507.   procedure TopLeftRowCol(var Row, Col : Word);
  508. Purpose
  509.   Return the Row and Col of the top left item.
  510. Description
  511.   This routine is primarily useful within a PreMove routine that
  512.   draws scrolling row and column headers. See TSPREAD2.PAS for an
  513.   example.
  514.  
  515.  
  516. ------- OPSPREAD Version History ------------------------------------
  517. Version 1.13   1/15/92
  518.   Initial release (synchronized with Object Professional 1.13)
  519.